<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class APNS_Push extends CI_Controller {

	private $fp = NULL;
	private $server;
	private $certificate;
	private $passphrase;

	private function __construct()
	{
		parent::__construct();
		$this->server = APNS_SERVER;
		$this->certificate = APNS_CERTIFICATE;
		$this->passphrase = APNS_PASSPHRASE;
	}
	
	private static $singleton_instance = null;
     
    public static function global_instance() {
        static $singleton_instance = null;
        if($singleton_instance === null) {
            $singleton_instance = new APNS_Push();
        }
         
        return($singleton_instance);
    }

	// This is the main loop for this script. It polls the database for new
	// messages, sends them to APNS, sleeps for a few seconds, and repeats this
	// forever (or until a fatal error occurs and the script exits).
	function start()
	{
		writeToLog('Connecting to ' . $this->server);

		if (!$this->connectToAPNS())
			exit;
		//$this->sendNotification("1", "23691b3ff44aeb0290a08c59ace87ac1d9fcf8d876feaa23b0e5ef203ac59ba2", "{\"aps\":{\"alert\":\"ccccccc ccccc\",\"sound\":\"default\"}}");
		
	}

	// Opens an SSL/TLS connection to Apple's Push Notification Service (APNS).
	// Returns TRUE on success, FALSE on failure.
	function connectToAPNS()
	{
		$ctx = stream_context_create();
		stream_context_set_option($ctx, 'ssl', 'local_cert', $this->certificate);
		stream_context_set_option($ctx, 'ssl', 'passphrase', $this->passphrase);
		//stream_context_set_option($ctx, 'ssl', 'cafile', 'entrust_2048_ca.cer');
		//$ctx = stream_context_create();
		//stream_context_set_option($ctx, 'ssl', 'local_cert', '/Users/Development/Dev/ck.pem');
		$this->fp = stream_socket_client(
			'ssl://' . $this->server, $err, $errstr, 60,
			STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);

		if (!$this->fp)
		{
			writeToLog("Failed to connect: $err $errstr");
			return FALSE;
		}

		writeToLog('Connection OK');
		return TRUE;
	}

	// Drops the connection to the APNS server.
	function disconnectFromAPNS()
	{
		fclose($this->fp);
		$this->fp = NULL;
	}

	// Attempts to reconnect to Apple's Push Notification Service. Exits with
	// an error if the connection cannot be re-established after 3 attempts.
	function reconnectToAPNS()
	{
		$this->disconnectFromAPNS();
	
		$attempt = 1;
	
		while (true)
		{
			writeToLog('Reconnecting to ' . $this->server . ", attempt $attempt");

			if ($this->connectToAPNS())
				return;

			if ($attempt++ > 3)
				fatalError('Could not reconnect after 3 attempts');

			sleep(60);
		}
	}

	// Sends a notification to the APNS server. Returns FALSE if the connection
	// appears to be broken, TRUE otherwise.
	function sendNotification($messageId, $deviceToken, $payload)
	{
	
		if (strlen($deviceToken) != 64)
		{
			writeToLog("Message $messageId has invalid device token");
			return TRUE;
		}

		if (strlen($payload) < 10)
		{
			writeToLog("Message $messageId has invalid payload");
			return TRUE;
		}

		writeToLog("Sending message $messageId to '$deviceToken', payload: '$payload'");

		if (!$this->fp)
		{
			writeToLog('No connection to APNS');
			return FALSE;
		}

		// The simple format
		$msg = chr(0)                       // command (1 byte)
		     . pack('n', 32)                // token length (2 bytes)
		     . pack('H*', $deviceToken)     // device token (32 bytes)
		     . pack('n', strlen($payload))  // payload length (2 bytes)
		     . $payload;                    // the JSON payload

		/*
		// The enhanced notification format
		$msg = chr(1)                       // command (1 byte)
		     . pack('N', $messageId)        // identifier (4 bytes)
		     . pack('N', time() + 86400)    // expire after 1 day (4 bytes)
		     . pack('n', 32)                // token length (2 bytes)
		     . pack('H*', $deviceToken)     // device token (32 bytes)
		     . pack('n', strlen($payload))  // payload length (2 bytes)
		     . $payload;                    // the JSON payload
		*/

		$result = @fwrite($this->fp, $msg, strlen($msg));
		echo $result; 
		if (!$result)
		{
			writeToLog('Message not delivered');
			return FALSE;
		}

		writeToLog('Message successfully delivered');
		return TRUE;
	}
}